<?PHP
	/************************************************************************
	*       This file, in addition to all other parts of X7 Chat are        *
	*                   Copyright 2003-2007 by Tim Chamness.                *
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Version:          X7 Chat 3.0.0-A2                                    *
	* Release Date:     October 20, 2007                                    *
	* Author:           Tim Chamness                                        *
	* Copyright:        2003-2007 by Tim Chamness                           *
	* Website:          http://www.x7chat.com/                              *
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* X7 Chat 3 is free software; you can redistribute it and/or modify     *
	* it under the terms of the GNU General Public License as published by  *
	* the Free Software Foundation; either version 3 of the License, or     *
	* (at your option) any later version.                                   *
	*                                                                       *
	* X7 Chat 3 is distributed in the hope that it will be useful, but      *
	* WITHOUT ANY WARRANTY; without even the implied warranty of            *
	* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
	* General Public License for more details.                              *
	*                                                                       *
	* You should have received a copy of the GNU General Public License     *
	* along with this program.  If not, see <http://www.gnu.org/licenses/>. *
	*                                                                       *
	* Additional Terms, compatible with Section 7 of the GNU GPL v3,        *
	* apply to X7 Chat 3 and all X7 Chat 3 files, including this one.       *
	* You should have received a copy of these Additional Terms in the      *
	* file docs/COPYING.ADDITIONAL_TERMS.  If you did not receive a copy    *
	* please visit <http://www.x7chat.com/COPYING.ADDITIONAL_TERMS/>.       *
	************************************************************************/
	
	// Record the start time of the script and define the current version
	$EXEC_START = microtime_float();
	$X7_CHAT_VERSION = array(1,'3.0.0 Alpha 2');
	define('IN_X7CHAT3',true);
	
	// Some initial setup stuff
	error_reporting(E_ALL &~ E_STRICT);
	set_error_handler('x7_handle_error');
	set_exception_handler('x7_exception_handler');
	set_time_limit(15);
	ignore_user_abort(true);
	setlocale(LC_CTYPE,'C');

	// Make sure magic_quotes are disabled, who would enable this?  At least PHP 6 gets rid of it.
	if(version_compare(PHP_VERSION,'6.0.0-dev','<')){
		set_magic_quotes_runtime(0);
		$GLOBALS['_STRIP_SLASHES'] = get_magic_quotes_gpc();
	}else{
		$GLOBALS['_STRIP_SLASHES'] = false;
	}
	
	// Load some required libraries
	require('./libraries/lib_output.php');
	require('./libraries/lib_input.php');
	require('./libraries/cache/filesystem.php');
	require('./libraries/data/filesystem.php');
	
	// Setup database
	$GLOBALS['DATABASE'] = new x7database();	// Used for long-term storage of data
	
	// Try to load settings
	try{
		$settings = $GLOBALS['DATABASE']->get('system','settings');
		$settings = $settings->get_next();
	}catch(dbexception $dbex){
		$settings = array();
	}
	
	// Make sure valid settings were loaded
	if(empty($settings)) trigger_error("Corrupted settings file detected",E_USER_ERROR);
	
	// One more library, this library handles integration
	require("./libraries/integration/{$settings['auth_handler']}.php");
	
	// Initialize the localization engine with the default language
	$GLOBALS['LANG'] = new language_controller($settings['default_lang']);
	
	// Setup cache, Used for temporary storage of data
	$GLOBALS['CACHE'] = new x7cache($settings['max_cache_size'],$settings['num_cache_preserve'],array('lib_logging','cache_event'));
	
	// Start user's session
	$ses = new lib_session();
	
	// Re-initialize the localization engine with the user's language preference
	$GLOBALS['LANG'] = new language_controller($ses->get_language());

	// Setup the template engine and some template variables
	$GLOBALS['OUTPUT'] = new lib_output($ses->get_theme());
	$GLOBALS['OUTPUT']->assign_by_ref('ses',$ses->session_string);
	$GLOBALS['OUTPUT']->assign_by_ref('settings',$settings);
	$GLOBALS['OUTPUT']->copyright = 'Powered By <a href="http://www.x7chat.com/" target="_blank">X7 Chat 3</a>; <a href="http://www.fsf.org/licensing/essays/free-sw.html" target="_blank">free software</a> released without warranty under the <a href="http://www.gnu.org/licenses/" target="_blank">GNU GPL</a>';
	
	// Check to see if the user has been banned
	if($ses->is_banned()){
		$GLOBALS['OUTPUT']->error_message = $GLOBALS['LANG']->lang_global->server_ban;
		echo tpl_errors::fatal_error();
		exit;
	}
	
	// Determine which page should be displayed.  The following array contains the pages
	// that are allowed, the boolean value of each index is true if the user must be logged
	// in to access the page.
	$action = x7in::read('act');
	$ALLOWED_ACTIONS = array(
				'login'=>false,
				'register'=>false,
				'verify'=>false,
				'recover'=>false,
				'calendar'=>false,
				'update'=>true,
				'chat'=>true,
				'roomlist'=>true,
				'pwreq'=>true,
				'create_room'=>true,
				'usersettings'=>true,
				'roomsettings'=>true,
				'view_log'=>true,
				'profile'=>true,
				'admin'=>true,
				);

	// Assign a default action if no action or an invalid action is passed
	if(empty($action) || !isset($ALLOWED_ACTIONS[$action]) || ($ses->user === null && $ALLOWED_ACTIONS[$action]))
		$action = $ses->user !== null ? 'chat' : 'login';

	// Check to see if the chatroom is disabled and whether the user can access it regardless
	if($settings['disable_chat'] && ($ses->user === null || !$ses->user->has_permission('OVERRIDE_DISABLE'))){
		$ALLOWED_ACTIONS = array('login','logout','error');
		if(!in_array($action,$ALLOWED_ACTIONS)){
			$GLOBALS['OUTPUT']->error_message = $GLOBALS['LANG']->lang_global->chatroom_disabled;
			echo tpl_errors::fatal_error();
			exit;
		}
	}
	
	header('content-type: text/html; charset=UTF-8');
	
	// Execute the page's source
	require("./sources/$action.php");

	//echo "/*".(microtime_float()-$EXEC_START)."*/";
	
	////////////////////////////////////////////////
	// Some core functions that are always needed //
	////////////////////////////////////////////////
	
	/**
	 * Generate a very precise timestamp as a string.
	 * It must be a string or bad things will happen when you run
	 * it through json_encode().
	 *
	 * @return A string containing the current timestamp with 
	 * to several decimal places accuracy.
	 */
	function microtime_float(){
		return (string)(microtime(true));
	}
	
	/**
	 * In the event of an uncaught exception, pass it to the error handler.
	 */
	function x7_exception_handler($exception){
		x7_handle_error(E_USER_ERROR,$exception->getMessage(),$exception->getFile(),$exception->getLine());
	}
	
	/**
	 * Handle Non-fatal PHP error messages:
	 *
	 * PHP Fatal Errors 	- These cannot be handled [E_ERROR, E_PARSE, E_CORE_*, E_COMPILE_*]
	 * PHP Non-fatal Errors	- Print a small error message on current page, open new page if in AJSON return environment [E_NOTICE, E_STRICT]
	 * PHP Non-fatal Errors - Treat as a fatal error, redirect to error page if in AJSON return environment, otherwise print message to screen and die [E_WARNING]
	 */
	function x7_handle_error($num,$text,$file,$line){
		
		if(!(error_reporting() & $num))
			return true;
		
		$output_html = !(isset($_GET['act']) && $_GET['act'] == 'update');
		
		if($num == E_STRICT || $num == E_NOTICE){
			if($output_html)
				echo "<b>Notice</b> - <i>$file:$line</i><br />$text<br /><br />";
			return true;
		}
		
		$msg = "<b>Fatal Error</b> - <i>$file:$line</i><br />$text";
		
		if(!$output_html)
			die('INDATA = [{"lastUpdateTime":0,"operation":"error_message","data":{"error_hash":"'.md5($msg).'","msg":"'.str_replace('"','&quot;',$msg).'"}}];');
			
		die($msg);
	}
	
	/**
	 * This will autoload lib_ and tpl_ classes the first time they are used.
	 *
	 * @param string $class The name of the class to load.
	 */
	function __autoload($class){
		
		if(strpos($class,'tpl_') === 0){
			
			// This function will trigger a fatal error if the template file cannot be loaded
			$GLOBALS['OUTPUT']->load_template_file($class);
				
		// Load a library file
		}elseif(strpos($class,'lib_') === 0){
			
			// Include this library file if it exists
			if(is_file("./libraries/{$class}.php"))
				require("./libraries/{$class}.php");
			// This is very bad, the chatroom installation appears to be corrupted
			else
				trigger_error("Could not load library class {$class}.",E_USER_ERROR);
			
		}else{
			trigger_error("Class {$class} is not defined.",E_USER_ERROR);
		}
		
	}
	
	/**
	 * Static class to clean incoming data
	 */
	class x7in {

		public static function utf8_strlen($string){
			return strlen(utf8_decode($string));
		}
		
		public static function utf8_strtolower($string){
			return utf8_encode(strtolower(utf8_decode($string)));
		}
		
		/**
		 * Get an incoming variable
		 *
		 * @param array $dvar The raw input array ($_GET,$_POST,$_COOKIE,etc.)
		 * @param string $dname The name of the variable to get
		 * @param string $strip_mode How to clean up the incoming data
		 * @return The cleaned variable
		 */
		public static function get(&$dvar,$dname,$strip_mode='f'){

			if(!isset($dvar[$dname])){
				switch($strip_mode){
					case 'a':
						return array();
					break;
					case 'i':
						return 0;
					break;
					default:
						return '';
					break;
				}
			}

			if($strip_mode == 'f')
				return x7in::strip_full($dvar[$dname]);
			elseif($strip_mode == 'f-n')
				return x7in::strip_full(str_replace("\n",'',$dvar[$dname]));
			elseif($strip_mode == 'a'){
				return is_array($t = x7in::strip_full($dvar[$dname])) ? $t : array();
			}elseif($strip_mode == 'i')
				return intval($dvar[$dname]);

			return x7in::strip_raw($dvar[$dname]);
		}
		
		public static function read($dname,$strip_mode='f'){
			return x7in::get($_REQUEST,$dname,$strip_mode);
		}

		/**
		 * Strips < ' chr(31) chr(30) \n \r
		 * Also strips slashes added by magic quotes
		 *
		 * @param mixed $data The data to clean up
		 * @return The cleaned up data
		 */
		public static function strip_full($data){
			if(is_numeric($data))
				return $data;
			elseif(!is_array($data)){

				if($GLOBALS['_STRIP_SLASHES']) $data = stripslashes($data);

				$data = str_replace("<","&lt;",
				str_replace("'",'&#039;',
				str_replace(chr(31),
				'<br />',str_replace(chr(30),
				'',str_replace("\n",'<br />',
				(str_replace("\r",
				'',$data)))))));

			}else{
				foreach($data as $key=>$val){
					$data[$key] = x7in::strip_full($val);
				}
			}

			return $data;
		}

		// Return the data raw, just strip out slashes added by magic quotes
		function strip_raw($data){
			global $stripslashes;

			if($stripslashes)
				$data = stripslashes($data);

			return $data;
		}

	}

	class lib_clientsidedb {
		var $data_cache;
		var $key;
		var $database;
		var $initial_id;

		function lib_clientsidedb($initial_id,$getby,$key,$database){
			$this->initial_id = $initial_id;
			$this->key = $key;
			$this->database = $database;
			
			if($getby == $key)
				$getby = $initial_id;
			else
				$getby = array($getby,$initial_id);
			
			$query = $GLOBALS['DATABASE']->get($database,$getby);
			$this->data_cache = $query->get_next();
			
			if(empty($this->data_cache))
				$this->data_cache = null;
		}

		function is_valid(){
			return $this->data_cache != null;
		}

		function commit_changes($changes=null){
			if($this->data_cache == null)
				$this->data_cache = $changes;
			elseif($changes !== null)
				$this->data_cache = array_merge($this->data_cache,$changes);
			
			$GLOBALS['DATABASE']->put($this->database,$this->data_cache);
			$GLOBALS['CACHE']->write('messages',array('lastUpdateTime'=>microtime_float(),'database'=>$this->database,'operation'=>'add','index'=>$this->data_cache[$this->key],'data'=>$this->get_output()));
		}

		function remove(){
			$GLOBALS['DATABASE']->remove($this->database,$this->data_cache[$this->key]);
		}
	}
	
	interface if_x7iterator {
		public function __construct($group,$controller);
		public function get_next();
	}
	
	interface if_x7cache {
		const WMODE_APPEND = 'a';
		const WMODE_REPLACE = 'w';
		public function __construct($optimize_at,$num_cache_preserve,$block_shrink=false);
		public function getline($cache_id,$line);
		public function remove($cache_id);
		public function remove_group($group_id);
		public function get_group_iterator($group_id);
		public function exists($cache_id);
		public function gettime($cache_id);
		public function settime($cache_id);
		public function write($cache_id,$data,$mode=self::WMODE_REPLACE);
	}
	
	interface if_x7database {
		public function get($table,$where=null,$fuzzy=false);
		public function put($table,$data);
		public function remove($table,$where);
		public function getsize($table);
		public function gettime($table,$where);
	}
?>